package Controllers

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"work1/DataBase"
	Model "work1/Models"
	service "work1/Service"

	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/ssh"
)

type ServerController struct {
	commonController
}

func (that *ServerController) Update(request *gin.Context) {
	type validatorStruct struct {
		ServerId int `uri:"server_id" binding:"required,number"`
	}
	var person validatorStruct
	if err := request.ShouldBindUri(&person); err != nil {
		that.commonController.SendFailResponse(request, err.Error())
		return
	}
	updata := make(map[string]interface{})
	request.ShouldBind(&updata)
	SessionModel := DataBase.DB.Begin()
	defer SessionModel.Rollback()
	if a := SessionModel.Model(&Model.Server{}).Where("id = ?", person.ServerId).Updates(updata).Error; a != nil {
		that.SendFailResponse(request, a.Error())
		return
	}
	SessionModel.Commit()
	that.SendSuccessResponse(request, "", "")
}

func (that *ServerController) BaseSoft(request *gin.Context) {
	type validatorStruct struct {
		ServerId int `form:"server_id" json:"server_id" binding:"required,number"`
	}
	if err := request.ShouldBind(&validatorStruct{}); err != nil {
		that.commonController.SendFailResponse(request, err.Error())
		return
	}
	Server := &Model.Server{}
	var ctx context.Context
	SessionModel := DataBase.DB.WithContext(ctx)
	ServerInfo := map[string]any{}
	SessionModel.Table(fmt.Sprintf("%v", Server.TableName())).Where("id = ?", request.Request.FormValue("server_id")).Find(&ServerInfo)
	if len(ServerInfo) == 0 {
		that.commonController.SendFailResponse(request, "服务器查询失败")
		return
	}

	//获取最新服务器信息
	if p, err := getIsp(ServerInfo["account_id"]); err != nil {
		that.SendFailResponse(request, "更新远程服务器信息失败："+err.Error())
		return
	} else {
		if info := p.GetEcsInfo(ServerInfo); info != nil {
			if err := SessionModel.Model(Server).Where("id = ?", request.Request.FormValue("server_id")).Updates(&info).Find(&ServerInfo).Error; err != nil {
				that.commonController.SendFailResponse(request, "更新远程服务器信息失败"+err.Error())
				return
			}
		} else {
			that.SendFailResponse(request, "更新远程服务器信息失败："+err.Error())
			return
		}
	}
	fmt.Println(4444)
	AuthModel := &Model.Authentication{}
	AuthInfo := map[string]any{}
	SessionModel.Table(fmt.Sprintf(" %v", AuthModel.TableName())).Where("id = ?", ServerInfo["auth_id"]).Find(&AuthInfo)
	if len(AuthInfo) == 0 {
		that.commonController.SendFailResponse(request, "服务器未配置登录方式")
		return
	}
	SshModel := service.SshStruct{}
	if status, err := SshModel.Conn(AuthInfo, fmt.Sprintf("%v", ServerInfo["ip_address"])); !status {
		that.commonController.SendFailResponse(request, err)
		return
	}
	defer SshModel.SshClient.Close()
	var output string
	var err any
	//检查系统版本是否支持

	if output, err = SshModel.Shell("sudo lsb_release -i"); err != nil {
		that.SendFailResponse(request, err.(string))
		return
	}

	Distributor := output
	if x := strings.Index(Distributor, "CentOS"); x != -1 {
		Distributor = "CentOS"
	} else if y := strings.Index(Distributor, "Ubuntu"); y != -1 {
		Distributor = "Ubuntu"
	} else {
		that.SendFailResponse(request, "检查系统：系统类型不支持"+Distributor)
		return
	}

	if output, err = SshModel.Shell("sudo uname -m"); err != nil {
		that.SendFailResponse(request, err.(string))
		return
	}

	machine := output
	if x, y := strings.Index(machine, "x86_64"), strings.Index(machine, "amd64"); x != -1 || y != -1 {
		machine = "64"
	} else if x, y := strings.Index(machine, "armv8"), strings.Index(machine, "aarch64"); x != -1 || y != -1 {
		machine = "arm64-v8a"
	} else {
		that.SendFailResponse(request, "检查系统：系统架构不支持"+machine)
		return
	}

	type check_struct struct {
		status bool
		msg    any
	}
	vpn_chan := make(chan check_struct)

	go func(ch chan check_struct, sshClient *ssh.Client, machine string) {
		a := check_struct{}
		a.status, a.msg = CheckVpn(sshClient, machine)
		ch <- a
		defer close(ch)
	}(vpn_chan, SshModel.SshClient, machine)

	nginx_chan := make(chan check_struct)

	go func(ch chan check_struct, sshClient *ssh.Client, Distributor string) {
		a := check_struct{}
		a.status, a.msg = CheckNginx(sshClient, Distributor)
		ch <- a
		defer close(ch)
	}(nginx_chan, SshModel.SshClient, Distributor)
	vpn := <-vpn_chan
	if !vpn.status {
		that.SendFailResponse(request, fmt.Sprintf("%v", vpn.msg))
		return
	}
	if x := <-nginx_chan; !x.status {
		that.SendFailResponse(request, fmt.Sprintf("%v", x.msg))
		return
	}
	SessionModel.Table(fmt.Sprintf(" %v", Server.TableName())).Where("id = ?", request.Request.FormValue("server_id")).UpdateColumn("vpn", vpn.msg)
	that.SendSuccessResponse(request, "检测通过", "")
}

func CheckNginx(sshClient *ssh.Client, Distributor string) (bool, any) {
	SshSession, err := sshClient.NewSession()
	if err != nil {
		return false, "检查nginx：首次打开会话失败" + err.Error()
	}
	output, _ := SshSession.CombinedOutput("sudo nginx -v")
	SshSession.Close()
	defer SshSession.Close()
	if a := strings.Index(string(output), "command not found"); a != -1 {

		cmd := "sudo yum install -y nginx"
		switch Distributor {
		case "CentOS":
			cmd = "sudo yum install -y nginx"
		case "Ubuntu":
			cmd = "sudo apt install -y nginx"
		}
		SshSession, err := sshClient.NewSession()
		if err != nil {
			return false, "安装nginx：打开会话失败" + err.Error()
		}
		SshSession.CombinedOutput(cmd)
		SshSession.Close()
		//安装完后再检查一次
		SshSession, err = sshClient.NewSession()
		if err != nil {
			return false, "检查nginx：安装是否成功时打开会话失败" + err.Error()
		}
		output, _ = SshSession.CombinedOutput("sudo nginx -v")
		SshSession.Close()
		if a := strings.Index(string(output), "command not found"); a != -1 {
			return false, "nginx 安装失败请手动安装" + string(output)
		}
	}

	SshSession, err = sshClient.NewSession()
	if err != nil {
		return false, "检查nginx：重启nginx时打开会话失败" + err.Error()
	}
	SshSession.CombinedOutput("sudo systemctl restart nginx ")
	SshSession.Close()

	SshSession, err = sshClient.NewSession()
	if err != nil {
		return false, "检查nginx：最后检查时打开会话失败" + err.Error()
	}

	check_status, _ := SshSession.CombinedOutput("sudo systemctl status nginx")
	SshSession.Close()
	if a := strings.Index(string(check_status), "active (running)"); a == -1 {
		return false, "检查nginx：一切就绪，无法启动，请手动排查" + string(check_status)
	}
	return true, nil
}

func CheckVpn(sshClient *ssh.Client, machine string) (bool, any) {
	SshSession, err := sshClient.NewSession()
	if err != nil {
		return false, "检查vpn：首次打开会话失败" + err.Error()
	}
	defer SshSession.Close()
	vpndir := "/usr/local/bin/v2ray"
	output, _ := SshSession.CombinedOutput("sudo " + vpndir + " -version")
	SshSession.Close()

	SshSession, err = sshClient.NewSession()
	if err != nil {
		return false, "检查vpn：获取远程用户目录时打开会话失败" + err.Error()
	}
	remotePathByte, _ := SshSession.CombinedOutput("pwd")
	SshSession.Close()
	remotePath := strings.TrimSpace(string(remotePathByte))

	if a := strings.Index(string(output), "command not found"); a == -1 {
		if status, msg := configureVpn(sshClient, remotePath); !status {
			return false, fmt.Sprintf("%v", msg)
		} else {
			return true, msg
		}
	}

	type uploadfile_struct struct {
		status bool
		msg    any
	}
	uploadScript := make(chan uploadfile_struct)
	go func(ch chan uploadfile_struct, sshClient *ssh.Client, remotePath string) {
		a := uploadfile_struct{}
		a.status, a.msg = service.UploadFile(sshClient, "./Resources/v2ray/install-release.sh", remotePath)
		ch <- a
		defer close(ch)
	}(uploadScript, sshClient, remotePath)

	uploadBase := make(chan uploadfile_struct)
	go func(ch chan uploadfile_struct, sshClient *ssh.Client, remotePath, machine string) {
		a := uploadfile_struct{}
		a.status, a.msg = service.UploadFile(sshClient, "./Resources/v2ray/v2ray-linux-"+machine+".zip", remotePath)
		ch <- a
		defer close(ch)
	}(uploadBase, sshClient, remotePath, machine)

	if x := <-uploadScript; !x.status {
		return x.status, "vpn脚本" + fmt.Sprintf("%v", x.msg)
	}
	if x := <-uploadBase; !x.status {
		return x.status, "vpn软件" + fmt.Sprintf("%v", x.msg)
	}

	SshSession, err = sshClient.NewSession()
	if err != nil {
		return false, "检查vpn：安装vpn时打开会话失败" + err.Error()
	}
	installVpnByte, _ := SshSession.CombinedOutput("sudo  echo y | sudo /bin/bash " + remotePath + "/install-release.sh -l " + remotePath + "/v2ray-linux-" + machine + ".zip")
	SshSession.Close()

	SshSession, err = sshClient.NewSession()
	if err != nil {
		return false, "检查vpn：安装完成确认时打开会话失败" + err.Error()
	}
	output, _ = SshSession.CombinedOutput("sudo " + vpndir + " -version")
	SshSession.Close()
	if a := strings.Index(string(output), "command not found"); a == -1 {
		if status, msg := configureVpn(sshClient, remotePath); !status {
			return false, fmt.Sprintf("%v", msg)
		} else {
			return true, msg
		}
	} else {
		return false, "vpn安装失败请手动安装" + string(installVpnByte)
	}
}
func configureVpn(sshClient *ssh.Client, remotePath string) (bool, any) {
	client_file, _ := ioutil.ReadFile("./Resources/v2ray/client.json")
	ip := sshClient.RemoteAddr().String()[0:strings.LastIndex(sshClient.RemoteAddr().String(), ":")]

	client := make(map[string]string)
	if err := json.Unmarshal(client_file, &client); err != nil {
		return false, "配置vpn：vpn客户端文件读取失败" + err.Error()
	}
	client["ps"] = ip
	client["add"] = ip
	vemss, _ := json.Marshal(client)
	vemss_link := "vmess://" + base64.StdEncoding.EncodeToString(vemss)

	if status, msg := service.UploadFile(sshClient, "./Resources/v2ray/config.json", remotePath); !status {
		return false, "配置vpn：上传配置文件时 " + fmt.Sprintf("%v", msg)
	}

	SshSession, err := sshClient.NewSession()
	if err != nil {
		return false, "配置vpn：移动配置文件时打开会话失败" + err.Error()
	}
	SshSession.CombinedOutput("sudo  mv " + remotePath + "/config.json /usr/local/etc/v2ray/config.json")
	defer SshSession.Close()
	SshSession.Close()

	SshSession, err = sshClient.NewSession()
	if err != nil {
		return false, "配置vpn：重启vpn时打开会话失败" + err.Error()
	}
	SshSession.CombinedOutput("sudo systemctl restart v2ray")
	SshSession.Close()

	SshSession, err = sshClient.NewSession()
	if err != nil {
		return false, "配置vpn：最后检查时打开会话失败" + err.Error()
	}
	check_status, _ := SshSession.CombinedOutput("sudo systemctl status v2ray")
	SshSession.Close()
	if a := strings.Index(string(check_status), "active (running)"); a == -1 {
		return false, "配置vpn：一切就绪，无法启动，请手动排查" + string(check_status)
	}
	return true, vemss_link
}

func (that *ServerController) ConfigureSSL(request *gin.Context) {
	type validatorStruct struct {
		ServerId int `form:"server_id" binding:"number"`
		DomainId int `form:"domain_id" binding:"required,number"`
	}
	if err := request.ShouldBind(&validatorStruct{}); err != nil {
		that.commonController.SendFailResponse(request, err.Error())
		return
	}
	Server := &Model.Server{}
	var ctx context.Context
	SessionModel := DataBase.DB.WithContext(ctx)
	ServerInfo := map[string]any{}
	SessionModel.Table(fmt.Sprintf("%v", Server.TableName())).Where("id = ?", request.Request.FormValue("server_id")).Find(&ServerInfo)
	if len(ServerInfo) == 0 {
		that.commonController.SendFailResponse(request, "服务器查询失败")
		return
	}
	AuthModel := &Model.Authentication{}
	AuthInfo := map[string]any{}
	SessionModel.Table(fmt.Sprintf(" %v", AuthModel.TableName())).Where("id = ?", ServerInfo["auth_id"]).Find(&AuthInfo)
	if len(AuthInfo) == 0 {
		that.commonController.SendFailResponse(request, "服务器未配置登录方式")
		return
	}

	DomainModel := Model.Domain{}
	DomainInfo := map[string]any{}
	SessionModel.Model(&DomainModel).Where("id = ?", request.Request.FormValue("domain_id")).Find(&DomainInfo)

	if len(DomainInfo) <= 0 {
		that.commonController.SendFailResponse(request, "域名查询失败")
		return
	}

	SshService := service.SshStruct{}
	if status, err := SshService.Conn(AuthInfo, fmt.Sprintf("%v", ServerInfo["ip_address"])); !status {
		that.commonController.SendFailResponse(request, err)
		return
	}
	defer SshService.SshClient.Close()
	if err := that.ConfigureServerSSL(&SshService, DomainInfo); err != nil {
		that.commonController.SendFailResponse(request, fmt.Sprintf("%v", err))
		return
	}
	that.commonController.SendSuccessResponse(request, "", "")
}

func (that *ServerController) ConfigureServerSSL(SshService *service.SshStruct, DomainInfo map[string]any) any {

	configCert := make(chan map[string]any)
	go func() {

		i := 0 //检查次数
		//检查证书路径
	checkcert:
		result, _ := SshService.Shell("sudo test -d /home/cert &&sudo echo $?")
		if len(result) == 0 {
			if i == 0 {
				SshService.Shell("sudo mkdir /home/cert/")
				i++
				goto checkcert
			} else {
				result, _ = SshService.Shell("sudo cd /home/cert/")
				configCert <- map[string]any{"status": false, "msg": "检查并尝试创建【证书路径】时发现" + result}
				return
			}
		}
		result, _ = SshService.Shell("sudo chmod -R 777 /home/cert/")
		if len(result) != 0 {
			configCert <- map[string]any{"status": false, "msg": "设置权限【证书路径】时发现：" + result}
			return
		}
		defer close(configCert)
	}()

	configLog := make(chan map[string]any)
	go func() {

		i := 0 //检查次数
		//检查日志路径
	checklogs:
		result, _ := SshService.Shell("sudo test -d /home/logs &&sudo echo $?")
		if len(result) == 0 {
			if i == 0 {
				SshService.Shell("sudo mkdir /home/logs/")
				i++
				goto checklogs
			} else {
				result, _ = SshService.Shell("sudo cd /home/logs/")
				configLog <- map[string]any{"status": false, "msg": "检查并尝试创建【日志路径】时发现" + result}
				return
			}
		}
		result, _ = SshService.Shell("sudo chmod -R 777 /home/logs/")
		if len(result) != 0 {
			configLog <- map[string]any{"status": false, "msg": "设置权限【日志路径】时发现：" + result}
			return
		}
		defer close(configLog)
	}()

	userDir, _ := SshService.Shell("pwd")
	sslpemfileName := fmt.Sprintf("%v.%v.pem", DomainInfo["RR"], DomainInfo["domain_name"])
	sslkeyfileName := fmt.Sprintf("%v.%v.key", DomainInfo["RR"], DomainInfo["domain_name"])
	//上传证书key
	uploadkey := make(chan map[string]any)
	go func() {
		//上传key证书
		ioutil.WriteFile(sslkeyfileName, []byte(fmt.Sprintf("%v", DomainInfo["ssl_key"])), 0666)
		defer os.Remove(sslkeyfileName)
		if status, err := service.UploadFile(SshService.SshClient, sslkeyfileName, userDir); !status {
			uploadkey <- map[string]any{"status": false, "msg": err}
			return
		}
		defer close(uploadkey)
	}()
	//上传证书pem
	uploadpem := make(chan map[string]any)
	go func() {
		//上传pem证书
		ioutil.WriteFile(sslpemfileName, []byte(fmt.Sprintf("%v", DomainInfo["ssl_pem"])), 0666)
		defer os.Remove(sslpemfileName)
		if status, err := service.UploadFile(SshService.SshClient, sslpemfileName, userDir); !status {
			uploadpem <- map[string]any{"status": false, "msg": err}
			return
		}
		defer close(uploadpem)
	}()
	if x := <-configCert; x["status"] == false {
		return fmt.Sprintf("%v", x["msg"])
	}
	if x := <-configLog; x["status"] == false {
		return fmt.Sprintf("%v", x["msg"])
	}
	if x := <-uploadkey; x["status"] == false {
		return fmt.Sprintf("%v", x["msg"])
	}
	if x := <-uploadpem; x["status"] == false {
		return fmt.Sprintf("%v", x["msg"])
	}

	remotePath := userDir + "/" + sslpemfileName
	SshService.Shell("sudo mv " + remotePath + " /home/cert")

	remotePath = userDir + "/" + sslkeyfileName
	SshService.Shell("sudo mv " + remotePath + " /home/cert")

	SshService.Shell("sudo chmod -R 777 /home/cert")

	proxy_conf, _ := ioutil.ReadFile("./Resources/vhost/proxy_conf.conf")

	host_name := fmt.Sprintf("%v.%v", DomainInfo["RR"], DomainInfo["domain_name"])
	proxy_conf_string := fmt.Sprintf(string(proxy_conf), host_name, host_name, host_name, host_name)
	host_name_file := host_name + ".conf"
	//上传vhost证书
	ioutil.WriteFile(host_name_file, []byte(proxy_conf_string), 0666)
	defer os.Remove(host_name_file)
	if status, err := service.UploadFile(SshService.SshClient, host_name_file, userDir); !status {
		return fmt.Sprintf("%v", err)
	}
	remotePath = userDir + "/" + host_name_file
	result, _ := SshService.Shell("sudo nginx -V")

	if a := strings.Index(string(result), "--prefix=/usr/local/nginx"); a != -1 {
		// /usr/local/nginx/conf/vhost
		SshService.Shell("sudo mv " + remotePath + " /usr/local/nginx/conf/vhost && sudo nginx -s reload")
	} else {
		// /etc/nginx/conf
		SshService.Shell("sudo mv " + remotePath + " /etc/nginx/conf.d && sudo systemctl restart nginx")
	}
	return nil
}

func (that *ServerController) CheckLockStatus(request *gin.Context) {
	type validatorStruct struct {
		ServerId int `form:"server_id" json:"server_id" binding:"required,number"`
	}

	if err := request.ShouldBind(&validatorStruct{}); err != nil {
		that.commonController.SendFailResponse(request, err.Error())
		return
	}

	Server := &Model.Server{}
	Task := &Model.Task{}
	result := map[string]interface{}{}
	var ctx context.Context
	DataBase.DB.Table(fmt.Sprintf(" %v as t1", Server.TableName())).Joins(fmt.Sprintf("INNER join %v as t2", Task.TableName())+" on t2.server_id = t1.id").Where("t2.status = ? and t1.id = ?", Model.TaskLockIng, request.Request.FormValue("server_id")).WithContext(ctx).Find(&result)
	if len(result) == 0 {
		that.commonController.SendSuccessResponse(request, result, "")
	} else {
		that.commonController.SendFailResponse(request, "服务器锁定中")
	}
}
